package cn.bqjr.eily.shiro.spring.boot;

import cn.bqjr.eily.shiro.spring.boot.entity.ShiroUser;
import cn.bqjr.eily.shiro.spring.boot.entity.TenantUserPwdToken;
import cn.bqjr.eily.utils.I18nUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 自定义认证授权中心
 */
public class UserPwdRealm extends AuthorizingRealm {

    @Autowired
    private IShiroUserService shiroUserService;

    @Autowired
    private ILoginRetryManager loginRetryMgr;

    /**
     * 授权(验证权限时调用)
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        ShiroUser sysUser = (ShiroUser) principals.getPrimaryPrincipal();
        Serializable userId = sysUser.getUserId();

        //用户角色列表
        List<String> userRolesIdList = shiroUserService.selectUserRoleIdList(userId);
        if(!CollectionUtils.isEmpty(userRolesIdList)) {
            Set<String> rolesSet = new HashSet<>(userRolesIdList.size());
            userRolesIdList.stream().forEach(roleId -> rolesSet.add(roleId));
            info.setRoles(rolesSet);
        }

        //用户权限列表
        Set<String> permsSet = shiroUserService.selectUserPerms(userId);
        info.setStringPermissions(permsSet);
        return info;
    }

    /**
     * 认证(登录时调用)
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken token) throws AuthenticationException {
        TenantUserPwdToken tenantUserPwdToken = (TenantUserPwdToken) token;
        String loginId = tenantUserPwdToken.getUsername();
        String tenantId = tenantUserPwdToken.getTenantId();

        //查询用户信息
        ShiroUser loginUser = shiroUserService.selectByLoginId(loginId);

        //账号不存在
        if (null == loginUser) {
            throw new UnknownAccountException(I18nUtils.getMsg("error.login.unknownAccount"));
        }

        //账号是否生效
        if (!loginUser.isValid()) {
            throw new DisabledAccountException(I18nUtils.getMsg("error.login.disabledAccount"));
        }

        if (loginUser.isLocked()) {
            throw new LockedAccountException(I18nUtils.getMsg("error.login.lockedAccount"));
        }

        loginRetryMgr.validate(loginId);

        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(
                loginUser, loginUser.getPassword(), getName());
        return info;
    }

    /**
     * 密码校验异常拦截
     *
     * @param token
     * @param info
     * @throws AuthenticationException
     */
    @Override
    protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) throws AuthenticationException {
        try {
            super.assertCredentialsMatch(token, info);
        } catch (IncorrectCredentialsException e) {
            TenantUserPwdToken tenantUserPwdToken = (TenantUserPwdToken) token;
            String loginId = tenantUserPwdToken.getUsername();
            Integer count = loginRetryMgr.increase(loginId);
            if (null != count) {
                throw new IncorrectCredentialsException(
                        I18nUtils.getMsg("error.login.incorrectCredentials.residual") + count, e);
            }
            throw new IncorrectCredentialsException(I18nUtils.getMsg("error.login.incorrectCredentials"), e);
        }
    }

    @Override
    public boolean supports(AuthenticationToken token) {
        return token != null && token instanceof TenantUserPwdToken;
    }
}
